package main

import (
	"embed"
	"encoding/base64"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	"gopkg.in/yaml.v3"
)

//go:embed index.html
var indexHtml embed.FS

type Content struct {
	Content string `json:"content"`
}

type ProxyItemStruct struct {
	Name     string `yaml:"name"`
	Type     string `yaml:"type"`
	Server   string `yaml:"server"`
	Port     int8   `yaml:"port"`
	Cipher   string `yaml:"cipher"`
	Password string `yaml:"password"`
	Udp      bool   `yaml:"udp"`
}

type ProxyGroupStruct struct {
	Name     string   `yaml:"name"`
	Type     string   `yaml:"type"`
	Proxies  []string `yaml:"proxies"`
	Url      string   `yaml:"url,omitempty"`
	Interval int      `yaml:"interval,omitempty"`
}

type ConfigStruct struct {
	Proxies     []ProxyItemStruct  `yaml:"proxies"`
	ProxyGroups []ProxyGroupStruct `yaml:"proxy-groups"`
}

func isValidURL(toTest string) bool {
	_, err := url.ParseRequestURI(toTest)
	if err != nil {
		return false
	}

	u, err := url.Parse(toTest)
	if err != nil || u.Scheme == "" || u.Host == "" {
		return false
	}

	return true
}

func parseGroup(nameList []string) []ProxyGroupStruct {
	group := []ProxyGroupStruct{}

	NCloudG := ProxyGroupStruct{
		Name:     "NCloud",
		Type:     "select",
		Url:      "",
		Interval: 0,
	}
	NCloudG.Proxies = []string{"自动选择", "故障转移"}
	NCloudG.Proxies = append(NCloudG.Proxies, nameList...)

	AutoG := ProxyGroupStruct{
		Name:     "自动选择",
		Type:     "url-test",
		Url:      "http://www.gstatic.com/generate_204",
		Interval: 86400,
	}
	AutoG.Proxies = nameList

	FallBackG := ProxyGroupStruct{
		Name:     "故障转移",
		Type:     "fallback",
		Url:      "http://www.gstatic.com/generate_204",
		Interval: 7200,
	}
	FallBackG.Proxies = nameList

	group = append(group, NCloudG)
	group = append(group, AutoG)
	group = append(group, FallBackG)

	return group
}

func parseURL(urlstr string) (string, error) {
	resp, err := http.Get(urlstr)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}

	decodedBytes, err := base64.StdEncoding.DecodeString(string(body))
	if err != nil {
		fmt.Println("解码过程中发生错误:", err)
		return "", err
	}

	// 将字节切片转换为字符串
	decodedString := string(decodedBytes)

	arr := strings.Split(decodedString, "\r\n")

	proxyItemS := []ProxyItemStruct{}
	nameList := []string{}

	for _, val := range arr {

		if val == "" {
			break
		}

		proxyItem := ProxyItemStruct{
			Udp: true,
		}

		arr2 := strings.Split(val, "@")
		v1 := arr2[0]
		v2 := arr2[1]

		arr21 := strings.Split(v1, "://")
		pswd, _ := base64.StdEncoding.DecodeString(arr21[1])
		pswdArr := strings.Split(string(pswd), ":")

		v21, _ := url.QueryUnescape(v2)
		arr31 := strings.Split(v21, "#")
		arr32 := strings.Split(arr31[0], ":")
		port, _ := strconv.ParseInt(arr32[1], 0, 8)

		proxyItem.Type = arr21[0]
		proxyItem.Cipher = pswdArr[0]
		proxyItem.Password = pswdArr[1]
		proxyItem.Server = arr32[0]
		proxyItem.Port = int8(port)
		proxyItem.Name = arr31[1]

		nameList = append(nameList, proxyItem.Name)

		proxyItemS = append(proxyItemS, proxyItem)
	}

	configS := ConfigStruct{}
	configS.Proxies = proxyItemS
	configS.ProxyGroups = parseGroup(nameList)

	j, _ := yaml.Marshal(configS)
	return string(j), nil
}

func main() {

	port := flag.String("port", "8080", "端口")
	debug := flag.Bool("debug", false, "调试模式")
	flag.Parse()

	if !*debug {
		gin.SetMode(gin.ReleaseMode)
	}

	r := gin.Default()

	r.NoRoute(func(c *gin.Context) {
		file, err := indexHtml.ReadFile("index.html")
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code":  1,
				"error": "未找到index.html",
			})
			return
		}
		c.Data(http.StatusOK, "text/html; charset=utf-8", file)
	})

	r.POST("/parse/clash_url", func(c *gin.Context) {

		var cont Content

		if err := c.BindJSON(&cont); err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code":  1,
				"error": err.Error(),
			})
			return
		}

		if !isValidURL(cont.Content) {
			c.JSON(http.StatusOK, gin.H{
				"code":  1,
				"error": "不是有效的url地址",
			})
			return
		}

		content, err := parseURL(cont.Content)

		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code":  1,
				"error": err,
			})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"code":    0,
			"message": content,
		})
	})

	r.Run(":" + *port)
}
